﻿/*
   Copyright 2011 Softisis

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FOM.Impl;
using FOM.Impl.Converters;
using FOM.Interfaces;
using NUnit.Framework;

namespace TestUnits
{
    /// <summary>
    /// For standard and nominal tests
    /// </summary>
    /// <remarks>Copyright Softisis 2011 (France)</remarks>
    [TestFixture]
    public class BasicMappingsTests
    {


        [SetUp]
        public void SetUp()
        {
        }

        [Test]
        public void TestFirstLevelCustomMapping()
        {
            // Tests that some fields


            // create manager
            IManager mngr = new ManagerImpl();

            IMappingNode<ObjectA, ObjectB> rootmap = mngr.RegisterMapping<ObjectA, ObjectB>("test");
            rootmap.Map(a => a.Entier, b => b.BUnobjectC.Val1);

            // srouce object
            var srcA = TestObjectsHolder.GetObjectA();

            // target object
            ObjectB tofill = new ObjectB();
            tofill.BChaine = "pouet"; // must be overwritten du to newinstance

            ObjectB res = (ObjectB)mngr.ApplyMapping("test", srcA, tofill);
            Assert.IsTrue(res != null);
            Assert.IsTrue(res.BUnobjectC != null);

            Assert.IsTrue(srcA.Entier.Equals(res.BUnobjectC.Val1));
            Assert.IsTrue(tofill.BChaine.Equals(res.BChaine));
        }


        [Test]
        public void TestFirstLevelCustomMappingNewInstance()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            IMappingNode<ObjectA, ObjectB> rootmap = mngr.RegisterMapping<ObjectA, ObjectB>("test").NewInstance();

            rootmap.Map(a => a.Entier, b => b.BUnobjectC.Val1);

            // source object
            var srcA = TestObjectsHolder.GetObjectA();

            // target object
            ObjectB tofill = new ObjectB();
            tofill.BChaine = "pouet"; // must be overwritten du to newinstance

            ObjectB res = (ObjectB)mngr.ApplyMapping("test", srcA, tofill);

            Assert.IsTrue(res != null);
            Assert.IsTrue(res.BUnobjectC != null);

            Assert.IsTrue(!tofill.BChaine.Equals(res.BChaine));
            Assert.IsTrue(srcA.Entier.Equals(res.BUnobjectC.Val1));
        }

        [Test]
        public void TestEnumConverter()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            IMappingNode<ObjectA, ObjectB> rootmap = mngr.RegisterMapping<ObjectA, ObjectB>("test");

            rootmap.Map(a => a.Chaine, b => b.BUnObjetA.UnEnum).UseConverter(new StringToValueTypesConv());

            // source object
            var srcA = TestObjectsHolder.GetObjectA();
            // we set this string to the enum value n°2
            srcA.Chaine = "" + MonEnum.UnDeuxiemeEnum;

            ObjectB res = (ObjectB)mngr.ApplyMapping("test", srcA);
            Assert.IsTrue(res.BUnObjetA.UnEnum == MonEnum.UnDeuxiemeEnum);
        }

        [Test]
        public void TestNullableEnumConverter()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            IMappingNode<ObjectA, ObjectB> rootmap = mngr.RegisterMapping<ObjectA, ObjectB>("test");

            rootmap.Map(a => a.Chaine, b => b.BUnNullableEnum).UseConverter(new StringToValueTypesConv());

            // source object
            var srcA = TestObjectsHolder.GetObjectA();
            // we set this string to the enum value n°2
            srcA.Chaine = "" + MonEnum.UnDeuxiemeEnum;

            ObjectB res = (ObjectB)mngr.ApplyMapping("test", srcA);
            Assert.IsTrue(res.BUnNullableEnum == MonEnum.UnDeuxiemeEnum);
        }

        [Test]
        public void Test1StLevelMapping()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            IMappingNode<ObjectA, ObjectA> rootmap = mngr.RegisterMapping<ObjectA, ObjectA>("test");

            rootmap.Map(a => a, aa => aa.UnObjetB.BUnObjetA).NewInstance()
                .UseConverter(new PropertyCopierConv())
                .Exclude(a => a.Anid, aa => aa.UnObjetB.BUnObjetA.Car) // must be ignored because ignored by converter
                .Exclude(a => a.Doubleobject, aa => aa.UnObjetB.BUnObjetA.Doubleobject) // must be ignored
                .Exclude(a => a.Chaine, aa => aa.Chaine); // taken in account!!

            // source object
            var srcA = TestObjectsHolder.GetObjectA();

            ObjectA res = (ObjectA)mngr.ApplyMapping("test", srcA);
            Assert.IsFalse(res == null);
            Assert.IsFalse(res.UnObjetB == null);
            Assert.IsFalse(res.UnObjetB.BUnObjetA == null);
            Assert.IsTrue(res.UnObjetB.BUnObjetA.Chaine == null);
            Assert.IsFalse(res.UnObjetB.BUnObjetA.Doubleobject == 0.0);
            Assert.IsFalse(res.UnObjetB.BUnObjetA.Anid == 0);


            // this test create submaps and map a 1st level property copier
            IMappingNode<ObjectA, ObjectA> rootmap2 = mngr.RegisterMapping<ObjectA, ObjectA>("test2");

            // create a target A to tes the exclusion

            ObjectA tgtA=new ObjectA();
            tgtA.UnObjetB=new ObjectB();
            tgtA.UnObjetB.BUnObjetA=new ObjectA();
            tgtA.UnObjetB.BUnObjetA.UnObjetB = new ObjectB(); // Will be replaced by "new instance"

            // 2n test: A mapping with mutliple inner maps
            var submap = rootmap2.Map(rootA => rootA, otherRootA => otherRootA.UnObjetB.BUnObjetA).Map(zeA => zeA.UnObjetB, otherA => otherA.UnObjetB).NewInstance();
            submap.UseConverter(new PropertyCopierConv(true))
                .Exclude(bsrc => bsrc.BCar, bdst => bdst.BCar);


            ObjectA res2 = (ObjectA)mngr.ApplyMapping("test2", srcA);
            Assert.IsNotNull(res2);
            Assert.IsNotNull(res2.UnObjetB);
            Assert.IsNotNull(res2.UnObjetB.BUnObjetA);
            Assert.IsNotNull(res2.UnObjetB.BUnObjetA.UnObjetB);

            Assert.IsTrue(res2.UnObjetB.BUnObjetA.UnObjetB.BCar == '\0');
            Assert.IsTrue(res2.UnObjetB.BUnObjetA.UnObjetB.BEntier != 0);
        }


        [Test]
        public void ComplexeMappings()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            IMappingNode<ObjectA, ObjectA> rootmap2 = mngr.RegisterMapping<ObjectA, ObjectA>("test2");
            // 2n test: A mapping with mutliple inner maps
            var submap = rootmap2.Map(a => a, aa => aa.UnObjetB.BUnObjetA) // root A is copied in aa.UnObjetB.BUnObjetA
                .Map(b => b.UnObjetB, bb => bb.UnObjetB); // then UnObjetB from root is copied to aa.UnObjetB.BUnObjetA.UnObjetB
            submap.Map(a => a.RandomVal, g => g.BEntier);
            submap.Map(a => a, g => g.BUnObjetA.UnObjetB); // brain twisting ...

            var srcA = TestObjectsHolder.GetObjectA();

            ObjectA res2 = (ObjectA)mngr.ApplyMapping("test2", srcA);

            Assert.IsFalse(res2 == null);
            Assert.IsFalse(res2.UnObjetB == null);
            Assert.IsFalse(res2.UnObjetB.BUnObjetA == null);
            Assert.IsFalse(res2.UnObjetB.BUnObjetA.Anid == 0);
            Assert.IsFalse(res2.UnObjetB.BUnObjetA.UnObjetB.BEntier == 0);
            Assert.IsTrue(res2.UnObjetB.BUnObjetA.UnObjetB.RandomVal == res2.UnObjetB.BUnObjetA.UnObjetB.BEntier);

            // Other one, to replace one value in the existing target
            var tgtA = TestObjectsHolder.GetObjectA();
            tgtA.UnObjetB.BEntier = 100; // will be replaced by source value
            tgtA.UnObjetB.BCar= 'R'; // will not be touched because not mapped
            
            tgtA.UnObjetB.BUnobjectC=new ObjectC(); // will not be touched because mapped, but excluded (sic)
            tgtA.UnObjetB.BUnobjectC.Val1 = 456789;

            // test of partial mapping, with a map + exclude
            IMappingNode<ObjectA, ObjectA> rootmap3 = mngr.RegisterMapping<ObjectA, ObjectA>("test3");
            rootmap3.Map(a => a.Entier, a => a.UnObjetB.BEntier);
            
            // stupid, but supported
            rootmap3.Map(a => a.UnObjetB.BUnobjectC, a => a.UnObjetB.BUnobjectC);
            rootmap3.Exclude(a => a.UnObjetB.BUnobjectC, a => a.UnObjetB.BUnobjectC);

            ObjectA res3 = (ObjectA)mngr.ApplyMapping("test3", srcA, tgtA);
            Assert.IsFalse(res3 == null);
            Assert.IsFalse(res3.UnObjetB == null);
            Assert.IsFalse(res3.UnObjetB.BEntier == 100); // must have been replaced du to mapping
            Assert.IsTrue(res3.UnObjetB.BCar == 'R'); // no mapped, so not touched
            Assert.IsFalse(res3.UnObjetB.BUnobjectC == null); // Not touched due to exclusion on a mapping
        }

        /// <summary>
        /// Test the converter that allows to reconnect children after mapping.
        /// Do it on a source object and a new instance object
        /// </summary>
        [Test]
        public void TestReconnectOriginalChildrenConverter()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            IMappingNode<ObjectA, ObjectA> rootmap = mngr.RegisterMapping<ObjectA>("test");

            // build object hierarchy
            var source = new ObjectA();
            source.Chaine = "A: SOURCE";
            source.UnObjetB=new ObjectB();
            source.UnObjetB.BChaine = "A.B: SOURCE";
            source.UnObjetB.BUnObjetA=new ObjectA();
            source.UnObjetB.BUnObjetA.Chaine = "A.B.A: SOURCE";
            source.UnObjetB.BUnObjetA.UnObjetB=new ObjectB();
            source.UnObjetB.BUnObjetA.UnObjetB.BUnObjetA=new ObjectA();
            source.UnObjetB.BUnObjetA.UnObjetB.BUnobjectC=new ObjectC();

            source.UnObjetB.BUnObjetA.UnObjetB.BChaine = "A.B.A.B: SOURCE";
            source.UnObjetB.BUnObjetA.UnObjetB.BUnObjetA.Chaine = "A.B.A.B.A: SOURCE (ignored)";
            source.UnObjetB.BUnObjetA.UnObjetB.BUnobjectC.CChaine = "A.B.A.B.C: SOURCE (ignored)";

            source.UnObjetB.BUnObjetA.UnObjetB.BUnobjectC.Val1 = 333; // will be xopied by mapping


            var target = new ObjectA();
            target.Chaine = "A: TGT";
            target.UnObjetB = new ObjectB();
            target.UnObjetB.BChaine = "A.B: TGT";
            target.UnObjetB.BUnObjetA = new ObjectA();
            target.UnObjetB.BUnObjetA.Chaine = "A.B.A: TGT";
            target.UnObjetB.BUnObjetA.UnObjetB = new ObjectB();
            target.UnObjetB.BUnObjetA.UnObjetB.BUnObjetA = new ObjectA();
            target.UnObjetB.BUnObjetA.UnObjetB.BUnobjectC = new ObjectC();

            target.UnObjetB.BUnObjetA.UnObjetB.BChaine = "I have been reconnected";
            target.UnObjetB.BUnObjetA.UnObjetB.BUnObjetA.Chaine = "A.B.A.B.A: TGT (reconnected)";
            target.UnObjetB.BUnObjetA.UnObjetB.BUnobjectC.CChaine = "A.B.A.B.C TGT (reconnected)";

            target.UnObjetB.BUnObjetA.UnObjetB.BUnobjectC.Val1 = 666; // will be replaced by mapping

            rootmap.Map(s => s.UnObjetB.BUnObjetA.UnObjetB, d => d.UnObjetB.BUnObjetA.UnObjetB)
                .UseConverter(new ReconnectOriginalChildrenConv())
                .NewInstance() // mandatory with ReconnectOriginalChildrenConv
                .Map(o=>o.BUnobjectC.Val1); // we will replace one value anymore

            var res = (ObjectA)mngr.ApplyMapping("test", source, target);

            Assert.IsTrue(res != null);
            Assert.IsTrue(res.Chaine == "A: TGT");

            Assert.IsTrue(res.UnObjetB != null);
            Assert.IsTrue(res.UnObjetB.BChaine == "A.B: TGT");
            
            Assert.IsTrue(res.UnObjetB.BUnObjetA != null);
            Assert.IsTrue(res.UnObjetB.BUnObjetA.Chaine == "A.B.A: TGT");

            Assert.IsTrue(res.UnObjetB.BUnObjetA.UnObjetB != null);
            Assert.IsTrue(res.UnObjetB.BUnObjetA.UnObjetB.BChaine == "I have been reconnected");

            Assert.IsTrue(res.UnObjetB.BUnObjetA.UnObjetB.BUnObjetA != null);
            Assert.IsTrue(res.UnObjetB.BUnObjetA.UnObjetB.BUnobjectC != null);

            Assert.IsTrue(res.UnObjetB.BUnObjetA.UnObjetB.BUnObjetA.Chaine == "A.B.A.B.A: TGT (reconnected)");
            Assert.IsTrue(res.UnObjetB.BUnObjetA.UnObjetB.BUnobjectC.CChaine == "A.B.A.B.C TGT (reconnected)");

            Assert.IsTrue(res.UnObjetB.BUnObjetA.UnObjetB.BUnobjectC.Val1==333); // value hab bee replaced by mapping

        }

    }
}
